#include "master.h"
#include "slave_protocol.h"
#include "slave.h"
#include "master_internal_cmd.h"



#include "../base/kvcfg.h"
#include "../base/mq.h"
#include "../base/list.h"
#include "../base/map.h"
#include "../base/th.h"
#include "../base/dl.h"


#include <assert.h>
#include <stdio.h>

C_CODE_BEGIN

struct master_cfg_file {
	struct kv_element* pars;
	/* default initialization order sequence 
	 * This cmds area will hold all the initialization
	 * command for the master component in its execution
	 * sequence.
	 */
	struct list* cmds;
};



static struct master_cfg_file* _master_build_cfg( const char* fn ) {
	struct master_cfg_file* cfgf = malloc(sizeof(struct master_cfg_file));
	const char* c;
	cfgf->pars = kv_element_load_script(fn);
	/* Now loading the commands sequence into the list */
	cfgf->cmds = list_create( sizeof(const char*) , 10 );
	if( cfgf->pars == NULL )
		fprintf(stderr,"Configuration script loading error : %s.\nUsing default configuration parameter to initialize!",fn);
	/* Add as commands as execution order */
#define ADD_COMMAND(name) \
	c = name; \
	list_push_back(cfgf->cmds,(void*)(&c))


	ADD_COMMAND(_cmd_add_notification_slave_name());
	ADD_COMMAND(_cmd_load_slave_name());
	ADD_COMMAND(_cmd_add_thread_name());
	
#undef ADD_COMMAND

	return cfgf;
}




static void _master_exe_cfg_foreach( void* node,void* others ) {
	const char* c = *(const char**)node;
	struct master* m = (struct master*)others;
	struct kv_element* kv;
	struct master_command* cm;
	if( m->cfg_cmd->pars )
		kv = kv_element_query( m->cfg_cmd->pars , c );
	else
		kv = NULL;
	/* Here we should not care about the kv value, if the kv value is NULL , the corresponding
	 * command routine should be able to provide the default value for each command need to execute */
	cm = (struct master_command*)map_query(m->cmdmap,c,NULL);
	assert(cm);
	cm->cmd(m,kv);
}

static void _master_exe_cfg( struct master* m ) {
	list_foreach(m->cfg_cmd->cmds,_master_exe_cfg_foreach,m);
}

static void _master_exe_cmd( struct master* m , const char* cmd , struct kv_element* val ) {
	struct master_command* mcmd = (struct master_command*)map_query(m->cmdmap,cmd,NULL);
	int ret;
	if( mcmd == NULL ) {
		fprintf(stderr,"Unrecognized command %s \n",cmd);
	} else {
		ret = mcmd->cmd( m , val );
		if( ret <0 ) {
			fprintf(stderr,"Configuration command %s execution error.\n\n",cmd);
		}
	}
}


static void _master_reg_cmd( struct master* m , const char* cmd , struct master_command* mcd ) {
	struct map_kv kv;
	void* ret;
	kv.key = cmd;
	kv.val = mcd;
	ret = map_insert(m->cmdmap,&kv,NULL);
	assert(ret);
}

/* 
 * This main thread routine for master to fetch message from the queue and than
 * invoke the callback function.
 */
static void _master_send_message( struct master* m , struct message* msg, struct slave* s) {
	struct msgqueue* mq = _slave_ref_msgqueue(s);
	msgqueue_enqueue(mq,msg);
}

void _master_send_message_by_slavename( struct master* m ,struct message* msg, const char* slavename ) {
	struct slave* sl = *(struct slave**)map_query(m->slavemap,slavename,NULL);
	if( sl != NULL )
		_master_send_message(m,msg,sl);
}

size_t _master_send_msg( struct message* msg, const char* dest , struct slave_api* sapi) {
	msg->src = _slave_name(_slave_api_ref_slave(sapi));
	msg->sid = _slave_new_sid(_slave_api_ref_slave(sapi));
	_master_send_message_by_slavename(_slave_api_ref_master(sapi),msg,dest);
	return msg->sid;
}

static void _master_join_thread( void* node,void* others ) {
	thread_join(*((thread_handler*)(node)), NULL , NULL );
}


/* 
 * This function will internally destroy (clean) all the resource owned by the
 * master. Therefore , this function can only be invoked once the join operation
 * has been done properly , which means all the resource own by the thread has been
 * detached. 
 * In productive program ,the clean operation is not very useful when it comes to
 * free memory .
 * Additionally , this function will invoke all the clean callback once the slave
 * has been detached from the master
 */


static void _master_destroy_foreach( struct map_kv* mkv , void* args ) {
	struct slave* sl = (struct slave*)mkv->val;
	struct master* ms= (struct master*)(args);
	/* call the clean routine */
	_slave_clean_invoke(sl);
	/* destroy the related msg queue */
	msgqueue_destroy(_slave_ref_msgqueue(sl));
	/* unload the slave here */
	_slave_unload(sl);
}

static void _master_destroy( struct master* m ) {
	map_foreach(m->slavemap,_master_destroy_foreach,m);
	/* clean all the master related resource */
	map_destroy(m->slavemap);
	map_destroy(m->cmdmap);
	list_destroy(m->thpool);
	msgqueue_destroy(m->glb_msgqueue);
	if( m->cfg_cmd->pars )
		kv_element_destroy(m->cfg_cmd->pars);
	list_destroy(m->cfg_cmd->cmds);
	free(m->cfg_cmd);
}

EXPORT_FUNC void master_shutdown( struct master* m ) {
	strcpy((char*)m->thread_cmd,"EXIT");
	/* Join all the thread */
	list_foreach(m->thpool,_master_join_thread,NULL);
	/* Clean the memory and other stuff here */
	_master_destroy(m);
}

EXPORT_FUNC void master_join_forever( struct master* m ) {
	/* Join all the thread without notifying the exit command */
	list_foreach(m->thpool,_master_join_thread,NULL);
}

EXPORT_FUNC void master_run( struct master*m ) {
	/* Currently DO NOTHING */
}



EXPORT_FUNC struct master* master_create( const char* cfg ) {
	struct master* m;
	struct master_command cmd;
	m = malloc( sizeof(struct master) );
	/* create global message queue */
	m->glb_msgqueue = msgqueue_create(20,sizeof(struct slave*));
	/* create slave map */
	m->slavemap = map_create(sizeof(struct slave*),20,NULL);
	/* create thread pool */
	m->thpool = list_create(sizeof(dl_handler),12);
	/* create command map */
	m->cmdmap = map_create(sizeof(struct master_command),4,NULL);
	/* initialize thread cmd */
	strcpy((char*)m->thread_cmd,"");
	/* register master configuration command */

#define MASTER_ADD_CMD(name,cb) \
	cmd.cmd = cb; \
	_master_reg_cmd(m,name,&cmd)

	MASTER_ADD_CMD(_cmd_add_thread_name(),_cmd_add_thread);
	MASTER_ADD_CMD(_cmd_load_slave_name(),_cmd_load_slave);
	MASTER_ADD_CMD(_cmd_add_notification_slave_name(),_cmd_add_notification_slave);

#undef MASTER_ADD_CMD
	/* building the configuration command structure */
	m->cfg_cmd = _master_build_cfg(cfg);
	/* configure the master and than starts to run */
	_master_exe_cfg(m);
	return m;
}



C_CODE_END




